package main

import (
	"fmt"
	"strconv"
	"strings"
	"time"
)

// 在main函数之前执行，每一个源文件都可以包含一个init函数
func init() {
	fmt.Println("init函数执行了")
}

// 定义全局匿名函数
var FUNC01 = func(num1 int, num2 int) int {
	return num1 * num2
}

func main() {
	/*
	   	【3】基本语法
	   		func   函数名（形参列表)（返回值类型列表）{
	                       执行语句..
	                       return + 返回值列表
	   	} */

	sum := cal(10, 20)
	fmt.Println("cal函数调用两数相加：", sum)

	// sum2, _ := cal2(60,80); _表示忽略一个其中某一个返回值
	sum2, result := cal2(60, 80)
	fmt.Println("cal2函数调用两数相加：", sum2)
	fmt.Println("cal2函数调用两数差集：", result)

	//函数也是一种数据类型，可以赋值给一个变量，则该变量就是一个函数类型的变量了。通过该变量可以对函数调用。
	//函数也是一种数据类型，可以赋值给一个变量
	a := test ///变量就是一个函数类型的变量
	//a的类型是：func(int),test函数的类型是：func(int)
	fmt.Println("a的类型是: %T,test函数的类型是: %T \n", a, test)
	//通过该变量可以对函数调用 等价于test(10)
	a(10)

	// 函数既然是一种数据类型，因此在Go中，函数可以作为形参，并且调用
	// 调用test02函数
	test02(10, 3.19, test)
	test02(10, 3.19, a)

	// 自定义数据类型 基本语法: type 自定义数据类型名  数据类型
	type myInt int
	var my myInt = 20
	fmt.Println("自定义函数名的值 my = ", my)
	test03(10, test)

	//支持对函数返回值命名
	sum1, sub1 := test05(20, 10)
	fmt.Println("相加和是 sum1 = ", sum1)
	fmt.Println("相减差集是 sub1 = ", sub1)

	fmt.Println("全局匿名函数=============================================================")
	fmt.Println("")

	//定义匿名函数：定义的同时调用
	result01 := func(num1 int, num2 int) int {
		return num1 + num2
	}(10, 20)
	fmt.Println("匿名函数之 result01 = ", result01)

	//将匿名函数赋给一个变量，这个变量实际就是函数类型的变量
	//sub等价于匿名函数
	sub2 := func(num1 int, num2 int) int {
		return num1 - num2
	}
	result02 := sub2(30, 70)
	fmt.Println("result02 = ", result02)

	result03 := FUNC01(10, 10)
	fmt.Println("全局匿名函数 result03 = ", result03)

	fmt.Println("defer 关键字======================================================")
	fmt.Println("")

	fmt.Println(add(30, 60))

	fmt.Println("字符串相关函数======================================================")
	fmt.Println("")
	str := "golang你好"
	fmt.Println("字符串长度为 ", len(str))

	r := []rune(str)
	for i := 0; i < len(r); i++ {
		fmt.Printf("%c \n", r[i])
	}

	te, _ := strconv.Atoi("666")
	fmt.Println("字符串转数字 te = ", te)

	count := strings.Count("golangand", "an")
	fmt.Println("统计一个字符串有几个指定的子串 count = ", count)

	is := strings.EqualFold("go", "Go")
	fmt.Println("不区分大小写比较 is = ", is)

	fmt.Println("时间和日期的函数======================================================")
	fmt.Println("")
	//时间和日期的函数，需要到入time包，所以你获取当前时间，就要调用函数Now函数：
	now := time.Now()
	//Now()返回值是一个结构体，类型是：time.Time
	fmt.Printf("%v ~~~ 对应的类型为：%T\n", now, now)

	//调用结构体中的方法：
	fmt.Printf("年：%v \n", now.Year())
	fmt.Printf("月：%v \n", now.Month())      //月：February
	fmt.Printf("月：%v \n", int(now.Month())) //月：2
	fmt.Printf("日：%v \n", now.Day())
	fmt.Printf("时：%v \n", now.Hour())
	fmt.Printf("分：%v \n", now.Minute())
	fmt.Printf("秒：%v \n", now.Second())

	fmt.Printf("当前年月日： %d-%d-%d 时分秒：%d:%d:%d  \n", now.Year(), now.Month(),
		now.Day(), now.Hour(), now.Minute(), now.Second())
	//Sprintf可以得到这个字符串，以便后续使用：
	datestr := fmt.Sprintf("当前年月日： %d-%d-%d 时分秒：%d:%d:%d  \n", now.Year(), now.Month(),
		now.Day(), now.Hour(), now.Minute(), now.Second())
	fmt.Println(datestr)

	//这个参数字符串的各个数字必须是固定的，必须这样写
	datestr2 := now.Format("2006/01/02 15/04/05")
	fmt.Println(datestr2)
	//选择任意的组合都是可以的，根据需求自己选择就可以（自己任意组合）。
	datestr3 := now.Format("2006 15:04")
	fmt.Println(datestr3)

	fmt.Println("闭包相关===============================================================")
	foo := getProTimer(foo) // 针对foo函数的耗时统计装饰函数
	foo()
	foo()

	// 计算求和
	f := getSum()
	fmt.Println("闭包求和", f(1))
	fmt.Println("闭包求和", f(2))
}

// getNum返回值为一个函数，这个函数的参数是一个int类型的参数，返回值也是int类型
func getSum() func(int) int {
	var sum int = 0
	return func(num int) int {
		sum += num
		return sum
	}
}

func getProTimer(f func()) func() {
	return func() {
		start := time.Now().Unix()
		f()
		end := time.Now().Unix()
		fmt.Println("cost timer:", end-start)
	}
}

func foo() {
	fmt.Println("foo功能开始")
	time.Sleep(time.Second * 2)
	fmt.Println("foo功能结束")
}

// defer 关键字
func add(num1 int, num2 int) (num int) {
	// 在go中，程序遇到defer关键字，不会立即执行defer后的语句，而是将defer语句压入一个栈中，然后继续执行函数后面的语句
	defer fmt.Println("num1 = ", num1)
	defer fmt.Println("num2 = ", num2)
	// 栈的特点是：先进先出
	// 函数执行完毕以后，从栈中取出语句开始执行，按照先进后出的顺序执行语句
	var sum int = num1 + num2
	fmt.Println(sum)
	return
}

// (sum int, sub int) 对函数返回值命名，里面顺序就无所谓了，顺序不用对应
func test05(num1 int, num2 int) (sum int, sub int) {
	sub = num1 - num2
	sum = num1 + num2
	return
}

type myFunc func(int)

func test03(sum int, testFunc myFunc) {
	fmt.Println("-----test03")
}

func test02(num int, num2 float32, testFunc func(int)) {
	fmt.Println("-----test02")
}

func test(sum int) {
	fmt.Println(sum)
}

// 形参列表 变量放前面-数据类型放后面
func cal(num1 int, num2 int) int {
	var sum int = 0
	sum += num1
	sum += num2
	return sum
}

// (num1 int,num2 int)形参列表 变量放前面-数据类型放后面
// (int, int) 返回两个int类型实参
func cal2(num1 int, num2 int) (int, int) {
	var sum int = 0
	sum += num1
	sum += num2
	var result int = num1 - num2
	return sum, result
}
